class FileAnalyzer {
    int totalLinesInFile = 0
    int emptyLines = 0
    int comments = 0
    int complexity = 0
    int numberOfFunctions = 0
    int functionsLines = 0
    int relatedMethods

    String className

    private Map<String, List<String>> functionLists = [:]
    private List<String> filedNames = []

    private int openedLogicalBlocks = 0
    private boolean multiLineCommentOpened = false
    private lastMethodName = ""

    FileAnalyzer(File file) {
        calculateNLOC(file)
        calculateLcom()
    }

    private calculateNLOC(File file) {
        file.eachLine { currentLine ->
            totalLinesInFile++
            def dontCount = false
            if (currentLine.contains('*/')) {
                comments++
                multiLineCommentOpened = false
                dontCount = true
            } else if (currentLine.contains('/*')) {
                comments++
                multiLineCommentOpened = true
                dontCount = true
            } else if (currentLine.length() == 0) {
                emptyLines++
                dontCount = true
            } else if (currentLine.contains('//')) {
                comments++
                dontCount = true
            } else if (multiLineCommentOpened) {
                comments++
                dontCount = true
            }
            if (!dontCount) {
                if (currentLine.contains('{')) {
                    if (a == 0) {
                        b = exctractName(currentLine)
                    }
                    if (c == 1) {
                        addMethod(currentLine)
                    }
                    d++

                } else if (h == 1) {
                    attachFieldsToMethod(currentLine)
                } else if (openedLogicalBlocks >= 2) {
                    functionsLines++
                    findFields(currentLine)
                    calculateComplexity(currentLine)
                }

                if (containsBlockEnd(currentLine)) {
                    openedLogicalBlocks--
                }
            }
        }
    }

    private containsBlockEnd(String currentLine) {
        currentLine.contains('}')
    }


    private addMethod(String currentLine) {
        numberOfFunctions++
        def name = exctractName(currentLine)
        if (!(className && name == className && name.contains("get") && name.contains("set"))) {
            lastMethodName = name
            functionLists.put([lastMethodName, []])
        }

    }

    private attachFieldsToMethod(String currentLine) {
        def currentFieldName = exctractName(currentLine)
        if (currentFieldName) {
            filedNames << currentFieldName
        }
    }

    private findFields(currentLine) {
        filedNames.each { field ->
            if (currentLine.contains(field)) {
                functionLists[lastMethodName] << field
            }
        }
    }

    private calculateComplexity(currentLine) {
        ['if', 'else', '?.', '.each', '*.', '||', '&&', 'while', 'for', 'case'].each { operator ->
            if (currentLine.contains(operator)) {
                complexity++
            }
        }
    }


    private String exctractName(String currentLine) {
        currentLine.split(' ').findAll { !['private', 'static', 'public', 'protected', ''].contains(it) }[1]
    }




    private calculateLcom() {
        for (fun1 in functionLists) {
            for (fun2 in functionLists) {
                if (fun1.key != fun2.key) {
                    for (filed in fun2.value) {
                        if (fun1.value.contains(filed)) {
                            relatedMethods++
                            break
                        }
                    }
                }
            }
        }
    }

    public Map calculateResult() {
        return [
                relatedMethods: relatedMethods,
                complexity: complexity,
                numberOfFunctions: numberOfFunctions,
                summaryLines: totalLinesInFile,
                emptyLines: emptyLines,
                comments: comments,
                codeLines: totalLinesInFile - emptyLines - comments]
    }
}
